package com.example.ojdemo1.compile;

import com.example.ojdemo1.common.FileUtil;
import org.springframework.util.StringUtils;

import java.io.File;
import java.util.Queue;
import java.util.UUID;

/**
 * 处理运行和编译的过程
 */
public class Task {

    //表示所有临时文件所在目录
    private String WORK_DIR = null;
    //约定代码的类名
    private String CLASS = null;
    //约定要编译的代码文件名
    private String CODE = null;
    //约定存放编译错误的信息的文件名
    private String COMPILE_ERROR = null;
    //约定存放运行时的 标准输出 文件名
    private String STDOUT = null;
    //约定存放运行时的 标准错误 文件名
    private String STDERR = null;

    /**
     * 每个请求都要有不同的临时目录，就不能使用静态常量
     */
    public Task() {
        WORK_DIR = "./tmp/" + UUID.randomUUID().toString() + "/";
        CLASS = "Solution";
        CODE = WORK_DIR + "Solution.java";
        COMPILE_ERROR = WORK_DIR + "compileError.txt";
        STDOUT = WORK_DIR + "stdout.txt";
        STDERR = WORK_DIR + "stderr.txt";
    }

    /**
     * 用来处理编译和运行
     * @param question 要编译的源代码
     * @return 表示编译运行的结果，例如 编译错误/运行错误/运行正确...
     */
    public Answer compileAndRun(Question question) {
        Answer answer = new Answer();
        //0. 准备好用来存放临时文件的目录
        File workDir = new File(WORK_DIR);
        if(!workDir.exists()) {
            //不存在就创建多级目录
            workDir.mkdirs();
        }
        // TODO 进行安全判定
        //1.把 question 中的 code 写入到 Solution。java 文件中
        FileUtil.writeFile(CODE, question.getCode());
        //2.创建子进程，调用 javac 进行编译
        String compileCmd = String.format("javac -encoding utf8 %s -d %s", CODE, WORK_DIR);
        System.out.println("编译命令:" + compileCmd);
        CommandUtil.run(compileCmd, null, COMPILE_ERROR);
        //如果编译出错了，错误信息就被记录到 COMPILE_ERROR 这个文件中了，如果没有编译出错，这个文件是空文件
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if(StringUtils.hasLength(compileError)) {
            //编译出错: 返回Answer，里面记录错误信息
            System.out.println("编译出错!");
            answer.setError(1);
            answer.setReason(compileError);
            return answer;
        }
        //到了这里，说明编译正确，继续执行以下逻辑
        //3.创建子进程，调用 java 命令并执行
        String runCmd = String.format("java -classpath %s %s", WORK_DIR, CLASS);
        System.out.println("运行命令: " + runCmd);
        CommandUtil.run(runCmd, STDOUT, STDERR);
        //运行程序后，会把 java 子进程的标准输出和标准错误获取到. stdout.txt stderr.txt
        //这里就检查一下 stderr.txt 文件中是否有东西，有内容的话就说明运行出错
        String runError = FileUtil.readFile(STDERR);
        if(StringUtils.hasLength(runError)) {
            System.out.println("运行出错!");
            answer.setError(2);
            answer.setReason(runError);
            return answer;
        }
        //4.父进程获取到编译结果，打包成 Answer 对象
        answer.setError(0);
        answer.setStdout(FileUtil.readFile(STDOUT));
        return answer;
    }

    public static void main(String[] args) {
        Task task = new Task();
        Question question = new Question();
        question.setCode("public class Solution {\n" +
                "        public static void main(String[] args) {\n" +
                "            System.out.println(\"hello world!\");\n" +
                "        }\n" +
                "    }");
        Answer answer = task.compileAndRun(question);
        System.out.println(answer);
    }

}
